home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_58 / sb.c < prev    next >
C/C++ Source or Header  |  1995-01-01  |  19KB  |  643 lines

  1. /*
  2.  * Play and record digitized sound sample on soundblaster DAC/ADC using DMA.
  3.  * This source code is in the public domain.
  4.  * 
  5.  * Modification History
  6.  *
  7.  *  9-Nov-93    David Baggett       Wrote it based on Sound Blaster
  8.  *              <dmb@ai.mit.edu>    Freedom project and Linux code.
  9.  *
  10.  *  24-Jun-94   Gerhard Kordmann    - modified for recording facilities
  11.  *                                  - added keyboard safety-routines to
  12.  *                                    allow stopping of playing/recording
  13.  *                                  - removed click while buffer switch 
  14.  *                                  - added bugfixes by Grzegorz Jablonski
  15.  *                                    and several safety checks
  16.  *                                  - added free dosmem at end of program
  17.  *              <grzegorz@kmm-lx.p.lod.edu.pl>
  18.  
  19.  *  08-Jul-94   Gerhard Kordmann    - click also removed in recording
  20.  *                                  - changes from dosmem... to memcpy
  21.  *  03-Sep-94   Gerhard Kordmann    - added Highspeed DMA for frequencies
  22.  *                                    above 37000 Hz (also in sb.h)
  23.  *                                  - removed memcpy due to new dpmi-style
  24.  *                                    of djgpp
  25.  *              <kordmann@ldv01.Uni-Trier.de>
  26.  */
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #include <stdio.h>
  30. #include <dos.h>
  31. #include <string.h>
  32. #include <pc.h>
  33. #include "sb.h"
  34.  
  35.  
  36. /*  GO32 DPMI structs for accessing DOS memory. */
  37. static _go32_dpmi_seginfo dosmem;   /* DOS (conventional) memory buffer */
  38.  
  39. static _go32_dpmi_seginfo oldirq_rm;    /* original real mode IRQ */
  40. static _go32_dpmi_registers rm_regs;
  41. static _go32_dpmi_seginfo rm_si;    /* real mode interrupt segment info */
  42.  
  43. static _go32_dpmi_seginfo oldirq_pm;    /* original prot-mode IRQ */
  44. static _go32_dpmi_seginfo pm_si;    /* prot-mode interrupt segment info */
  45.  
  46. /*  Card parameters  */
  47. unsigned int    sb_ioaddr;
  48. unsigned int    sb_irq;
  49. unsigned int    sb_dmachan;
  50.  
  51. /* Is a sound currently playing or recorded ? */
  52. volatile int    sb_dma_active = 0;
  53.  
  54. /* Conventional memory buffers for DMA. */
  55. static volatile int sb_bufnum = 0;
  56. static char     *sb_buf[2];
  57. static unsigned int sb_buflen[2];
  58.  
  59. /* Info about current sample */
  60. static unsigned char    *sb_curdata;    /* pointer to next bit of data */
  61. static unsigned long    sb_curlength;   /* total length length left to play */
  62. static int HIGHSPEED;                   /* flag for normal/highspeed DMA */
  63.  
  64.  
  65. /* DMA chunk size, in bytes.
  66.  * This parameter determines how big our DMA buffers are.  We play
  67.  * the sample by piecing together chunks that are this big.  This
  68.  * means that we don't have to copy the entire sample down into
  69.  * conventional memory before playing it.  (A nice side effect of
  70.  * this is that we can play samples that are longer than 64K.)
  71.  *
  72.  * Setting this is tricky.  If it's too small, we'll get lots
  73.  * of interrupts, and slower machines might not be able to keep
  74.  * up.  Furthermore, the smaller this is, the more grainy the
  75.  * sound will come out.
  76.  *
  77.  * On the other hand, if we make it too big there will be a noticeable
  78.  * delay between a call to sb_play and when the sound actually starts
  79.  * playing, which is unacceptable for things like games where sound
  80.  * effects should be "instantaneous".
  81.  *
  82.  */
  83. #define DMA_CHUNK (16000)
  84.  
  85.  
  86. /* Interrupt handler for recording
  87.  *
  88.  * This is called in both protected mode and in real mode -- this means
  89.  * we don't have to switch modes when we service the interrupt.
  90.  */
  91. void sb_intr_rec(_go32_dpmi_registers *reg)
  92. {
  93.     register unsigned n = sb_bufnum;        /* buffer we just recorded */
  94.  
  95.     inportb(sb_ioaddr + SB_DSP_DATA_AVAIL); /* Acknowledge soundblaster */
  96.  
  97.     sb_rec_buffer(1 - n);                   /* Start next buffer recording */
  98.     
  99.     sb_empty_buffer(n);                     /* Save this buffer */
  100.     
  101.     outportb(0x20, 0x20);                   /* Acknowledge the interrupt */
  102.  
  103.     enable();
  104. }
  105.  
  106. /* Save buffer n in sb_curdata and calculate size for next time */
  107. void sb_empty_buffer(register unsigned n)
  108. {
  109.     if(sb_buflen[n] > 0) {
  110.          dosmemget((unsigned long) sb_buf[n], sb_buflen[n], sb_curdata);
  111.         sb_curdata += sb_buflen[n];
  112.         sb_curlength -= sb_buflen[n];
  113.  
  114.         /* for determination of buffersize keep in mind, that right now  */
  115.         /*  the other buffer is recording up to DMA_CHUNK bytes. Only if */
  116.         /*  - after saving this other buffer - there is still work to do,*/
  117.         /*  then this buffer can take the rest in the next round         */
  118.         if (sb_curlength > DMA_CHUNK) {
  119.             if (sb_curlength > 2*DMA_CHUNK)
  120.                 sb_buflen[n] = DMA_CHUNK;
  121.             else
  122.                 sb_buflen[n] = sb_curlength - DMA_CHUNK;
  123.         }
  124.         else 
  125.             sb_buflen[n] = 0;
  126.     }
  127. }
  128.  
  129. void sb_rec_buffer(register unsigned n)
  130. {
  131.     int     t;
  132.     unsigned char   im, tm;
  133.     
  134.     if (sb_buflen[n] == 0) {                /* See if we're already done */
  135.         sb_dma_active = 0;
  136.         return;
  137.     }
  138.     disable();
  139.  
  140.     im = inportb(0x21);                     /* Enable interrupts on PIC */
  141.     tm = ~(1 << sb_irq);
  142.     outportb(0x21,im & tm);
  143.  
  144.     outportb(SB_DMA_MASK, 5);               /* Set DMA mode to 'record' */
  145.     outportb(SB_DMA_FF, 0);
  146.     outportb(SB_DMA_MODE, 0x45);
  147.     
  148.     sb_bufnum = n;                          /* Set transfer address */
  149.     t = (int) ((unsigned long) sb_buf[n] >> 16) ;
  150.     outportb(SB_DMAPAGE + 3, t & 0xFF);
  151.     t = (int) ((unsigned long) sb_buf[n] & 0xFFFF);
  152.     outportb(SB_DMA + 2 * sb_dmachan, t & 0xFF);
  153.     outportb(SB_DMA + 2 * sb_dmachan, t >> 8);
  154.                                             /* Set transfer length byte count */
  155.     outportb(SB_DMA + 2 * sb_dmachan + 1, (sb_buflen[n]-1) & 0xFF);
  156.     outportb(SB_DMA + 2 * sb_dmachan + 1, (sb_buflen[n]-1) >> 8);
  157.  
  158.     outportb(SB_DMA_MASK, sb_dmachan);      /* Unmask DMA channel */
  159.  
  160.     enable();
  161.  
  162.     if(HIGHSPEED) {
  163.     sb_writedac(SB_SET_BLOCKSIZE);  /* prepare block programming */
  164.     }
  165.     else {
  166.     sb_writedac(SB_DMA_ADC);        /* command byte for DMA ADC transfer */
  167.     }
  168.  
  169.  
  170.     sb_writedac((sb_buflen[n]-1) & 0xFF);       /* sb_write length */
  171.     sb_writedac((sb_buflen[n]-1) >> 8);
  172.  
  173.     if(HIGHSPEED)
  174.     sb_writedac(SB_HIGH_DMA_8_BIT_ADC);  /* command byte for high speed DMA ADC transfer */
  175.  
  176.     sb_dma_active = 1;                  /* A sound is recorded now. */
  177. }
  178.  
  179. /* Record a sample through the ADC using DMA. */
  180. /* return number of bytes actually recorded */
  181. unsigned long sb_rec(unsigned char *data, unsigned long length)
  182. {
  183.     sb_install_interrupts(sb_intr_rec); /* Install our interrupt handlers */
  184.  
  185.     sb_curdata = data;                      /* Prime the buffers */
  186.     sb_curlength = length;
  187.     if(length > DMA_CHUNK)
  188.         sb_buflen[0] = DMA_CHUNK;
  189.     else
  190.         sb_buflen[0] = length;
  191.  
  192.     if(length <= DMA_CHUNK)
  193.         sb_buflen[1] = 0;
  194.     else
  195.         if(length > 2*DMA_CHUNK)
  196.             sb_buflen[1] = DMA_CHUNK;
  197.         else
  198.             sb_buflen[1] = length-DMA_CHUNK;
  199.     
  200.     sb_rec_buffer(0);               /* Start the first buffer recording.    */
  201.     while (sb_dma_active)
  202.         if(kbhit()) {               /* kbhit crashed sometimes              */
  203.         int rest;
  204.             rest = sb_read_counter();       /* samples still to record      */
  205.         if(HIGHSPEED)
  206.         sb_reset();                 /* writedac blocked in HS mode  */
  207.         else
  208.         sb_writedac(SB_HALT_DMA);   /* stop playing                 */
  209.             rest = sb_buflen[sb_bufnum] - rest;/* samples already recorded  */
  210.             length -= (sb_curlength - rest);/* total samples recorded       */
  211.             sb_buflen[sb_bufnum] = rest;    /* save those samples           */
  212.             sb_empty_buffer(sb_bufnum);
  213.             sb_dma_active = 0;              /* and exit the loop            */
  214.             sb_buflen[0] = sb_buflen[1] = sb_curlength = 0; /* clean up     */
  215.         }
  216.     sb_cleanup_ints();                   /* remove interrupts */
  217.     return length;
  218. }
  219.  
  220.  
  221. void sb_intr_play(_go32_dpmi_registers *reg)
  222. {
  223.     register unsigned n = sb_bufnum;        /* buffer we just played    */
  224.  
  225.     inportb(sb_ioaddr + SB_DSP_DATA_AVAIL); /* Acknowledge soundblaster */
  226.     
  227.     sb_play_buffer(1 - n);                  /* Start next buffer player */
  228.     
  229.     sb_fill_buffer(n);          /* Fill this buffer for next time around */
  230.     
  231.     outportb(0x20, 0x20);                   /* Acknowledge the interrupt */
  232.  
  233.     enable();
  234. }
  235.  
  236. /* Fill buffer n with the next data. */
  237. void sb_fill_buffer(register unsigned n)
  238. {
  239.     if (sb_curlength > DMA_CHUNK) {
  240.         sb_buflen[n] = DMA_CHUNK;
  241.         dosmemput(sb_curdata, DMA_CHUNK, (unsigned long) sb_buf[n]);
  242.         sb_curlength -= DMA_CHUNK;
  243.         sb_curdata += DMA_CHUNK;
  244.     }
  245.     else if (sb_curlength <= 0) {
  246.         sb_buflen[n] = 0;
  247.         sb_curlength = 0;
  248.     }
  249.     else {
  250.         sb_buflen[n] = sb_curlength;
  251.         dosmemput(sb_curdata, sb_curlength, (unsigned long) sb_buf[n]);
  252.         sb_curdata += sb_curlength;
  253.         sb_curlength = 0;
  254.     }
  255. }
  256.  
  257. void sb_play_buffer(register unsigned n)
  258. {
  259.     int     t;
  260.     unsigned char   im, tm;
  261.     if (sb_buflen[n] <= 0) {                /* See if we're already done */
  262.         sb_dma_active = 0;
  263.         return;
  264.     }
  265.     disable();
  266.  
  267.     im = inportb(0x21);                     /* Enable interrupts on PIC */
  268.     tm = ~(1 << sb_irq);
  269.     outportb(0x21,im & tm);
  270.  
  271.     outportb(SB_DMA_MASK, 5);               /* Set DMA mode 'play' */
  272.     outportb(SB_DMA_FF, 0);
  273.     outportb(SB_DMA_MODE, 0x49);
  274.  
  275.     sb_bufnum = n;                          /* Set transfer address */
  276.     t = (int) ((unsigned long) sb_buf[n] >> 16);
  277.     outportb(SB_DMAPAGE + 3, t);
  278.     t = (int) ((unsigned long) sb_buf[n] & 0xFFFF);
  279.     outportb(SB_DMA + 2 * sb_dmachan, t & 0xFF);
  280.     outportb(SB_DMA + 2 * sb_dmachan, t >> 8);
  281.                                         /* Set transfer length byte count */
  282.     outportb(SB_DMA + 2 * sb_dmachan + 1, (sb_buflen[n]-1) & 0xFF);
  283.     outportb(SB_DMA + 2 * sb_dmachan + 1, (sb_buflen[n]-1) >> 8);
  284.  
  285.     outportb(SB_DMA_MASK, sb_dmachan);      /* Unmask DMA channel */
  286.  
  287.     enable();
  288.  
  289.     if(HIGHSPEED) {
  290.     sb_writedac(SB_SET_BLOCKSIZE);      /* prepare block programming */
  291.     }
  292.     else {
  293.     sb_writedac(SB_DMA_8_BIT_DAC);      /* command byte for DMA DAC transfer */
  294.     }
  295.  
  296.     sb_writedac((sb_buflen[n]-1) & 0xFF);   /* sb_write length */
  297.     sb_writedac((sb_buflen[n]-1) >> 8);
  298.  
  299.     if(HIGHSPEED)
  300.     sb_writedac(SB_HIGH_DMA_8_BIT_DAC);  /* command byte for high speed DMA DAC transfer */
  301.  
  302.     sb_dma_active = 1;                       /* A sound is playing now. */
  303. }
  304.  
  305. /* Play a sample through the DAC using DMA. */
  306. void sb_play(unsigned char *data, unsigned long length)
  307. {
  308.  
  309.     sb_install_interrupts(sb_intr_play);    /* Install our interrupt handlers */
  310.     
  311.     sb_curdata = data;                      /* Prime the buffers */
  312.     sb_curlength = length;
  313.  
  314.     sb_fill_buffer(0);
  315.     sb_fill_buffer(1);
  316.     
  317.     sb_play_buffer(0);                  /* Start the first buffer playing.    */
  318.     while (sb_dma_active) {                 /* keyboard overflow crashes    */
  319.     if(kbhit()) {                           /*  so did kbhit()              */
  320.             if(getch() == 27) {
  321.         if(HIGHSPEED)
  322.             sb_reset();                 /* writedac blocked in HS mode */
  323.         else
  324.                     sb_writedac(SB_HALT_DMA);
  325.                 sb_dma_active = 0;
  326.                 sb_buflen[0] = sb_buflen[1] = sb_curlength = 0;
  327.             }
  328.             else
  329.                 kbclear();
  330.         }
  331.     }
  332.     sb_cleanup_ints();                           /* remove interrupts */
  333. }
  334.  
  335. /* Set sampling/playback rate.
  336.  * Parameter is rate in Hz (samples per second).
  337.  */
  338. void sb_set_sample_rate(unsigned int rate)
  339. {
  340.     unsigned char tc;
  341.  
  342.     if(rate > 37000) HIGHSPEED = 1;
  343.     else HIGHSPEED = 0;
  344.     if(HIGHSPEED)
  345.     tc = (unsigned char) ((65536 - 256000000/rate) >> 8);
  346.     else
  347.     tc = (unsigned char) (256 - 1000000/rate);
  348.  
  349.     sb_writedac(SB_TIME_CONSTANT);  /* Command byte for sample rate */
  350.     sb_writedac(tc);                /* Sample rate time constant */
  351. }
  352.  
  353. void sb_voice(int state)
  354. {
  355.     sb_writedac(state ? SB_SPEAKER_ON : SB_SPEAKER_OFF);
  356. }
  357.  
  358. /* Read soundblaster card parameters from BLASTER enivronment variable .*/
  359. void sb_getparams()
  360. {
  361.     char *t, *blaster;
  362.  
  363.     sb_ioaddr = 0x220;          /* Set arguments to Soundblaster defaults */
  364.     sb_irq = 7;
  365.     sb_dmachan = 1;
  366.  
  367.     t = getenv("BLASTER");
  368.     if (!t)
  369.         return;
  370.  
  371.     blaster = strdup(t);                /* Get a copy */
  372.  
  373.     t = strtok(blaster, " \t");         /* Parse the BLASTER variable */
  374.     while (t) {
  375.         switch (t[0]) {
  376.             case 'A':
  377.             case 'a':
  378.                 /* I/O address */
  379.                 sscanf(t + 1, "%x", &sb_ioaddr);
  380.                 break;
  381.             case 'I':
  382.             case 'i':
  383.                 /* IRQ */
  384.                 sb_irq = atoi(t + 1);
  385.                 break;
  386.             case 'D':
  387.             case 'd':
  388.                 /* DMA channel */
  389.                 sb_dmachan = atoi(t + 1);
  390.                 break;
  391.             case 'T':
  392.             case 't':
  393.                 /* what is this? */
  394.                 break;
  395.                 
  396.             default:
  397.                 printf("Unknown BLASTER option %c\n",t[0]);
  398.                 break;
  399.         }
  400.         t = strtok(NULL," \t");
  401.     }
  402.  
  403.     free(blaster);  
  404.     return;
  405. }
  406.  
  407. /* Init the soundblaster card. */
  408. /* gk : modified for autodetection */
  409. int sb_initcard(void)
  410. {
  411.     int i,j,error=0;
  412.     int NrOfBases = 6;
  413.     int Bases[] = {0x210,0x220,0x230,0x240,0x250,0x260};
  414.     int Base;
  415.  
  416.     for(i=0;i<NrOfBases && error == 0;i++) {
  417.         Base = Bases[i];
  418.         outportb(Base + SB_DSP_RESET,1);
  419.  
  420.         inportb(Base + SB_DSP_RESET);              /* Kill some time */
  421.         inportb(Base + SB_DSP_RESET);
  422.         inportb(Base + SB_DSP_RESET);
  423.         inportb(Base + SB_DSP_RESET);
  424.     
  425.         outportb(Base + SB_DSP_RESET, 0);
  426.         for(j=0;j<100;j++) {
  427.             if(sb_read_dac(Base) == 0xAA) {
  428.                 error = 1;
  429.                 break;
  430.             }
  431.         }
  432.     }
  433.     if(error == 0) 
  434.         Base = 0;
  435.     return(Base);
  436. }
  437.  
  438. int sb_read_dac(int Base)
  439. {
  440.     int i;
  441.  
  442.     for(i=0;i<10000;i++) {
  443.         if(inportb(Base + SB_DSP_DATA_AVAIL) & 0x080)
  444.             break;
  445.     }
  446.     return(inportb(Base+0x0A));
  447. }
  448.  
  449.  
  450. void sb_install_interrupts(void (*sb_intr)(_go32_dpmi_registers *))
  451. {
  452.     sb_install_rm_interrupt(sb_intr);
  453.     sb_install_pm_interrupt(sb_intr);
  454. }
  455.  
  456. /*
  457.  * Install our interrupt as the real mode interrupt handler for 
  458.  * the IRQ the soundblaster is on.
  459.  *
  460.  * We accomplish this by have GO32 allocate a real mode callback for us.
  461.  * The callback packages our protected mode code up in a real mode wrapper.
  462.  */
  463. void sb_install_rm_interrupt(void (*sb_intr)(_go32_dpmi_registers *))
  464. {
  465.     int ret;
  466.  
  467.     rm_si.pm_offset = (int) sb_intr;
  468.     ret = _go32_dpmi_allocate_real_mode_callback_iret(&rm_si, &rm_regs);
  469.     if (ret != 0) {
  470.         printf("cannot allocate real mode callback, error=%04x\n",ret);
  471.         exit(1);
  472.     }
  473.  
  474.     disable();
  475.     _go32_dpmi_get_real_mode_interrupt_vector(8 + sb_irq, &oldirq_rm);
  476.     _go32_dpmi_set_real_mode_interrupt_vector(8 + sb_irq, &rm_si);
  477.     enable();
  478. }
  479.  
  480. /* Remove our real mode interrupt handler. */
  481. void sb_cleanup_rm_interrupt()
  482. {
  483.     disable();
  484.     _go32_dpmi_set_real_mode_interrupt_vector(8 + sb_irq, &oldirq_rm);
  485. /* gk : added safety check */
  486.     if(rm_si.size != -1)
  487.         _go32_dpmi_free_real_mode_callback(&rm_si);
  488.     rm_si.size = -1;
  489.     enable();
  490. }
  491.  
  492. /* Install our interrupt as the protected mode interrupt handler for 
  493.  * the IRQ the soundblaster is on. */
  494. void sb_install_pm_interrupt(void (*sb_intr)(_go32_dpmi_registers *))
  495. {
  496.     int ret;
  497.     disable();
  498.  
  499.     pm_si.pm_offset = (int) sb_intr;
  500. /* changes to wrap by grzegorz */
  501.     ret = _go32_dpmi_allocate_iret_wrapper(&pm_si);
  502.     if (ret != 0) {
  503.         printf("cannot allocate protected mode wrapper, error=%04x\n",ret);
  504.         exit(1);
  505.     }
  506.  
  507.     pm_si.pm_selector = _go32_my_cs();
  508.     _go32_dpmi_get_protected_mode_interrupt_vector(8 + sb_irq, &oldirq_pm);
  509.     _go32_dpmi_set_protected_mode_interrupt_vector(8 + sb_irq, &pm_si);
  510.     enable();
  511. }
  512.  
  513. /* Remove our protected mode interrupt handler. */
  514. void sb_cleanup_pm_interrupt()
  515. {
  516.     disable();
  517. /* changes to wrap by grzegorz, safety chek by gk */
  518.     if(pm_si.size != -1)
  519.         _go32_dpmi_free_iret_wrapper(&pm_si);
  520.     pm_si.size = -1;
  521.  
  522.     _go32_dpmi_set_protected_mode_interrupt_vector(8 + sb_irq, &oldirq_pm);
  523.     enable();
  524. }
  525.  
  526. /* Allocate conventional memory for our DMA buffers.
  527.  * Each DMA buffer must be aligned on a 64K boundary in physical memory. */
  528. int sb_init_buffers()
  529. {
  530.     dosmem.size = 65536*3/16;
  531.     if (_go32_dpmi_allocate_dos_memory(&dosmem)) {
  532.         printf("Unable to allocate dos memory - max size is %lu\n", dosmem.size);
  533.         dosmem.size = -1;
  534.         return(0);
  535.     }
  536.  
  537.     (unsigned long) sb_buf[0] = dosmem.rm_segment * 16;
  538.     (unsigned long) sb_buf[0] += 0x0FFFFL;
  539.     (unsigned long) sb_buf[0] &= 0xFFFF0000L;
  540.     (unsigned long) sb_buf[1] = (unsigned long) sb_buf[0] + 0x10000;
  541.     return(1);
  542. }
  543.  
  544. /* Initliaze our internal buffers and the card itself to prepare
  545.  * for sample playing.
  546.  *
  547.  * Call this once per program, not once per sample. */
  548. int sb_init()
  549. {
  550.     memset(&rm_regs,0,sizeof(_go32_dpmi_registers));
  551.                         /* undefined registers cause trouble */
  552.     rm_si.size = -1;    /* to allow safety check before free */
  553.     pm_si.size = -1;
  554.     dosmem.size = -1;
  555.  
  556.     sb_getparams();     /* Card card params and initialize card. */
  557.     sb_ioaddr = sb_initcard();
  558.     
  559.     if(sb_ioaddr)
  560.         /* Allocate buffers in conventional memory for double-buffering */
  561.         if(!sb_init_buffers())
  562.             return 0;
  563.     return(sb_ioaddr);
  564. }
  565.  
  566.  
  567. /* Remove interrupt handlers */
  568. void sb_cleanup_ints()
  569. {
  570.     /*  Remove our interrupt handlers */
  571.     sb_cleanup_rm_interrupt();
  572.     sb_cleanup_pm_interrupt();
  573. }
  574.  
  575. /* leave no traces on exiting module
  576.  * Call this at end of program or if you won't need sb functions anymore
  577.  */
  578. int sb_cleanup()
  579. {
  580.     if(dosmem.size == -1)   /* There is nothing to free */
  581.     return(1);
  582.     if (!_go32_dpmi_free_dos_memory(&dosmem)) {
  583.         printf("Unable to free dos memory");
  584.         return(0);
  585.     }
  586.     return(1);
  587. }
  588.  
  589. int sb_read_counter(void)
  590. /* tells you how many bytes DMA play/recording have still to be done */
  591. {
  592.    outportb(SB_DMA_FF,0);
  593.    return(inportb(SB_DMA + 2 * sb_dmachan + 1) +
  594.           256*inportb(SB_DMA + 2 * sb_dmachan + 1));
  595. }
  596.  
  597.  
  598. void sb_reset(void)
  599. {
  600.     int j;
  601.     outportb(sb_ioaddr + SB_DSP_RESET,1);
  602.  
  603.     inportb(sb_ioaddr + SB_DSP_RESET);              /* Kill some time */
  604.     inportb(sb_ioaddr + SB_DSP_RESET);
  605.     inportb(sb_ioaddr + SB_DSP_RESET);
  606.     inportb(sb_ioaddr + SB_DSP_RESET);
  607.     
  608.     outportb(sb_ioaddr + SB_DSP_RESET, 0);
  609.     for(j=0;j<100;j++) {
  610.         if(sb_read_dac(sb_ioaddr) == 0xAA) {
  611.             break;
  612.         }
  613.     }
  614. }
  615.  
  616. void kbclear(void)
  617. {
  618.     short buffer;
  619.  
  620.     dosmemget(0x41a,sizeof(short),&buffer);
  621.     dosmemput(&buffer,2,0x41c);
  622. }
  623.  
  624.  
  625. /*
  626.  * Convenient wrappers for the sound functions
  627.  */
  628. void SoundPlay(int Rate, char *data, unsigned long length)
  629. {
  630.     sb_voice(1);
  631.     sb_set_sample_rate(Rate);
  632.     sb_play(data, length);
  633.     sb_voice(0);
  634. }
  635.  
  636.  
  637. unsigned long SoundRec(int Rate, char *data, unsigned long length)
  638. {
  639.     sb_set_sample_rate(Rate);
  640.     return(sb_rec(data, length));
  641. }
  642.  
  643.